
#this code is bluetooth uart service

import bluetooth
from ble_advertising import advertising_payload
from micropython import const

_IRQ_CENTRAL_CONNECT                 = const(1 << 0)
_IRQ_CENTRAL_DISCONNECT              = const(1 << 1)
_IRQ_GATTS_WRITE                     = const(1 << 2)
_IRQ_GATTS_READ_REQUEST              = const(1 << 3)

UART_UUID = bluetooth.UUID(0xfe00)
UART_TX = (bluetooth.UUID(0xfe01), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
UART_RX = (bluetooth.UUID(0xfe02), bluetooth.FLAG_WRITE,)
UART_SERVICE = (UART_UUID, (UART_TX,UART_RX,),)
SERVICES = (UART_SERVICE,)

# org.bluetooth.characteristic.gap.appearance.xml
_ADV_APPEARANCE_GENERIC_THERMOMETER = const(768)
        
class BLEUart:
    def __init__(self, ble, name='MPY-Uart'):
        self._ble = ble
        self._ble.active(True)
        self._ble.irq(handler=self._irq)
        ((self._TX_handle,self._RX_handle,),) = self._ble.gatts_register_services((UART_SERVICE,))
        self._connections = set()
        self._payload = advertising_payload(name=name, services=[UART_UUID], appearance=_ADV_APPEARANCE_GENERIC_THERMOMETER)
        self._advertise()
        #self.rxcallback = RxCB
        
    def RegistCallback(self, RxCB=None,ConnectCB=None,DisconnectCB=None):
        self.RxCallback = RxCB
        self.ConnectCallback = ConnectCB
        self.DisconnectCallback = DisconnectCB
    def _irq(self, event, data):
        # Track connections so we can send notifications.
        if event == _IRQ_CENTRAL_CONNECT:
            conn_handle, _, _, = data
            self._connections.add(conn_handle)
            print('_IRQ_CENTRAL_CONNECT')
            if self.ConnectCallback != None:
                self.ConnectCallback()            
        elif event == _IRQ_CENTRAL_DISCONNECT:
            conn_handle, _, _, = data
            self._connections.remove(conn_handle)
            print('_IRQ_CENTRAL_DISCONNECT')
            if self.DisconnectCallback != None:
                self.DisconnectCallback()
            # Start advertising again to allow a new connection.
            self._advertise()
        elif event == _IRQ_GATTS_WRITE:
            # 中央设备已写入此特征或描述符
            conn_handle, attr_handle = data
            rxbuf = self._ble.gatts_read(self._RX_handle)
            #print("Get command:",rxbuf)
            if self.RxCallback != None:
                self.RxCallback(rxbuf)
        elif event == _IRQ_GATTS_READ_REQUEST:
            # 中央设备已发出读请求. Note: 这是一个硬件IRQ
            # 返回None来拒绝读操作
            # Note: 这事件不支持 ESP32.
            conn_handle, attr_handle = data
            
    def ble_write(self, data, notify=False):
        self._ble.gatts_write(self._TX_handle, data)
        if notify:
            for conn_handle in self._connections:
                # Notify connected centrals to issue a read.
                self._ble.gatts_notify(conn_handle, self._TX_handle)

    def _advertise(self, interval_us=500000):
        self._ble.gap_advertise(interval_us, adv_data=self._payload)
        